﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using LED_Editor.Model;

namespace LED_Editor.ControllerProgramming {
	public class FramesConverter {
		private FramesConverter() { }

		private static bool _addEmptyFrameAtEnd = false;

		public static bool AddEmptyFrameAtEnd {
			get { return FramesConverter._addEmptyFrameAtEnd; }
			set { FramesConverter._addEmptyFrameAtEnd = value; }
		}


		private static FramesConverter _instance;
		public static FramesConverter Instance {
			get {
				if (_instance == null)
					_instance = new FramesConverter();
				return _instance;
			}
		}

		public FrameStruct[] Convert(IEnumerable<Frame> inputFrames) {
			List<FrameStruct> convertedFrames = new List<FrameStruct>();

			Frame[] frames = inputFrames.ToArray<Frame>();

			List<Frame> internalFrames = new List<Frame>();


			if (frames.Length < 2) {
				internalFrames.AddRange(frames);
			} else {
				for (int i = 0; i < frames.Length - 1; i++) {
					Frame current = frames[i];
					Frame next = frames[i + 1];

					if (current.TrackId != next.TrackId)
						throw new InvalidOperationException(string.Format("TrackId для кадров не совпадает: {0} -> {1}", current.TrackId, next.TrackId));

					if (!internalFrames.Contains(current)) {
						internalFrames.Add(current);
					}
					if (current.EndMarker != next.StartMarker) {
						Frame emptyFillFrame = Frame.CreateInstance();

						emptyFillFrame.StartMarker = current.EndMarker;
						emptyFillFrame.EndMarker = next.StartMarker;
						emptyFillFrame.StartColor = Color.Empty;
						emptyFillFrame.EndColor = Color.Empty;
						emptyFillFrame.TrackId = current.TrackId;
						internalFrames.Add(emptyFillFrame);
					}
					internalFrames.Add(next);
				}
			}

			foreach (var item in internalFrames) {
				convertedFrames.AddRange(Process(item));
			}

			if (AddEmptyFrameAtEnd) {
				FrameStruct sync = new FrameStruct();
				convertedFrames.Add(sync);
			}

			return convertedFrames.ToArray();
		}

		//public const int MaxFrameDuration = 65535;
		public static int MaxFrameDuration = UInt16.MaxValue;

		private FrameStruct[] Process(Frame inputFrame) {
			List<FrameStruct> frames = new List<FrameStruct>();
			if (inputFrame.FrameDuration == 0) {
				//do nothing
			} else if (inputFrame.FrameDuration < MaxFrameDuration) {
				FrameStruct mainFrame = inputFrame.ToFrameStruct();
				frames.Add(mainFrame);
				//FrameStruct restFrame = new FrameStruct()
				//{
				//    Duration = (byte)(MaxFrameDuration - mainFrame.Duration),
				//    StartR = 0,
				//    StartG = 0,
				//    StartB = 0,
				//    EndR = 0,
				//    EndG = 0,
				//    EndB = 0,
				//};
				//frames.AddRange(new[] { mainFrame, restFrame });
			} else if (inputFrame.FrameDuration > MaxFrameDuration) {
				int countOfFrames = (int)Math.Floor((double)inputFrame.FrameDuration / MaxFrameDuration);
				for (int i = 0; i < countOfFrames; i++) {
					FrameStruct mainFrame = new FrameStruct() {
						Duration = System.Convert.ToUInt16(MaxFrameDuration),
						StartColor = inputFrame.StartColor,
						EndColor = inputFrame.EndColor,
					};
					frames.Add(mainFrame);
				}
				if (inputFrame.FrameDuration > MaxFrameDuration * countOfFrames) {
					FrameStruct restFrame = new FrameStruct() {
						Duration = (byte)(inputFrame.FrameDuration - MaxFrameDuration * countOfFrames),
						StartColor = inputFrame.StartColor,
						EndColor = inputFrame.EndColor,
					};
					frames.Add(restFrame);
				}
				int frameDuration = 0;

				Color start = frames[0].StartColor;
				Color end = frames[frames.Count - 1].EndColor;


				for (int i = 0; i < frames.Count; i++) {
					//if (i >= frames.Count - 1)
					//    break;
					FrameStruct currentFrame = frames[i];

					if (!currentFrame.ColorsAreEmpty) {

						FrameStruct nextFrame = i >= frames.Count - 1 ? frames[i] : frames[i + 1];

						currentFrame.StartR = (byte)(start.R + (byte)(frameDuration * (end.R - start.R) / inputFrame.FrameDuration));
						currentFrame.StartG = (byte)(start.G + (byte)(frameDuration * (end.G - start.G) / inputFrame.FrameDuration));
						currentFrame.StartB = (byte)(start.B + (byte)(frameDuration * (end.B - start.B) / inputFrame.FrameDuration));


						currentFrame.EndR = (byte)(start.R + (byte)((frameDuration + currentFrame.Duration) * (end.R - start.R) / inputFrame.FrameDuration));
						currentFrame.EndG = (byte)(start.G + (byte)((frameDuration + currentFrame.Duration) * (end.G - start.G) / inputFrame.FrameDuration));
						currentFrame.EndB = (byte)(start.B + (byte)((frameDuration + currentFrame.Duration) * (end.B - start.B) / inputFrame.FrameDuration));
					}
					frames[i] = currentFrame;
					frameDuration += currentFrame.Duration;
				}
			} else {
				frames.Add(inputFrame.ToFrameStruct());
			}
			return frames.ToArray();

		}
	}
}
